// -*- coding: utf-8 -*-

/**
 * ex-hybrid2
 *
 * HybridSolverで繰り返し求解を行うことにより、
 * ハイブリッドシステムの実行を行う例。
 */

#include <iostream>
//#include <fstream>
#include <sstream>
//#include <string>

//#include "vnode.h"

#include "elisa/hybrid/hybrid.h"

#include "Falling.h"
#include "Falling1.h"
#include "Falling2.h"
//#include "GusBurner.h"
#include "Navigation.h"
#include "Navigation1.h"
//#include "Planer.h"
//#include "FallingInv.h"
#include "VanDerPol.h"
#include "Lorenz.h"
#include "Diode.h"

#include <time.h>

#ifdef _WIN32
#include <crtdbg.h>

//#include <windows.h>
//#include <mmsystem.h>
#endif

#define EXP

using namespace std;
using namespace elisa;

template<typename T>
void solve()
{
	T modeler;

	RealConstraint gc(gc<T>(modeler));
	ContConstraint fc(fc<T>(modeler));
	Model model(fc && gc);

/*
#ifndef EXP
	elisa::HybridSolver solver(model, fc);
#else
	// kludge for a bug in the destructor
	elisa::HybridSolver *solverPtr(new HybridSolver(model, fc));
	elisa::HybridSolver& solver(*solverPtr);
#endif
*/
	elisa::HybridSolver solver(model, fc);

	bool first(true);
	try {
		while (solver.next()) 
		{
			cout << "Solution " << solver.numSolutions()-1 << endl;

			cout << modeler << endl;

/*
#ifdef EXP
			break;
#endif
*/

			bool r = modeler.reset(first);

			if (first && r) first = false;
			if (!first && !r) break;
		}
	} catch (const string& msg) {
		cout << msg << endl;
	}

	if (!solver.numSolutions()) 
		cout << "NO SOLUTION" << endl;
	else 
		cout << solver.numSolutions() << " solution(s)" << endl;

#ifdef ELISA_PROFILE
	cout << "split:\t"  << solver.numSplit() << endl;
	cout << "boxes:\t"  << solver.numProcessedBoxes() << endl;
#endif
#ifdef HYBRID_PROFILE
	cout << "split(t): " << numSplitTime << endl;
	cout << "reduction:\t" << numHybridReduction << endl;
	cout << "narrow:\t" << numNarrow << endl;
	cout << "newton:\t" << numNewton << endl;
#endif
}


int main(int argc, char *argv[])
{
#ifdef _WIN32
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	//DWORD time;
	//time = timeGetTime();

	LARGE_INTEGER nFreq, nBefore, nAfter;
	DWORD dwTime;

	//変数の初期化
	memset(&nFreq,   0x00, sizeof nFreq);
	memset(&nBefore, 0x00, sizeof nBefore);
	memset(&nAfter,  0x00, sizeof nAfter);
	dwTime = 0;

	QueryPerformanceFrequency(&nFreq);
	QueryPerformanceCounter(&nBefore);
#endif
	clock_t before, after;
	double time;
	before = clock();

	// decide the number of steps to simulate.
	int n(1);
	if (argc == 2) {
		istringstream iss(argv[1]);
		iss >> n;
	}

	//RealVar::SetPrecision(1.0e-1);
	//RealVar::SetPrecision(1.0e-9);
	RealVar::SetDigits(20);
	Interval::SetOutputDigits(20);

	elisa::init();

	elisa::useBC5();
	//elisa::useBCNewton(); 
	//elisa::useBC3();
	//elisa::useHC4();
	//elisa::useHybrid();
	elisa::useHybridNewton(true);
	//elisa::useHybridNewton(false);

	for (int c(0); c < n; ++c) {
		cout << "step: " << c+1 << endl;

		if (c > 0) {
			odeOut.close();
			stringstream ss;
			ss << "ode-dump" << c << ".log";
			odeOut.open(ss.str().c_str(), std::ios::out);
		}

		//solve<Falling>();
		solve<Falling1>();
		//solve<Falling2>();
		//solve<Navigation>();
		//solve<Navigation1>();
		//solve<VanDerPol>();
		//solve<Lorenz>();
		//solve<Diode>();
	}

	elisa::dismiss();


	after = clock();
	time = (double)(after-before)/CLOCKS_PER_SEC;
#ifdef _WIN32
	//time = timeGetTime() - time;

	QueryPerformanceCounter(&nAfter);
	dwTime = (DWORD)((nAfter.QuadPart - nBefore.QuadPart)*1000 / nFreq.QuadPart);

	cout << "time (real): " << dwTime << endl;
#endif
	cout << "time (proc): " << time*1000 << endl;

	return 0;
}
